﻿
//
// Creator: Jickie阿文
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Windows.Forms;

namespace JBT.Library.Utilities
{
   public class KeyboardHook
    {
        static int hHook = 0;   
        public event System.Windows.Forms.KeyEventHandler KeyDown;
        public event KeyEventHandler KeyUp;   
  
        public const int WM_KEYBOARD_LL = 13;   
//      public const int WH_MOUSE_LL = 14;   
        public const int WM_KEYDOWN = 0x100;   
        public const int WM_KEYUP = 0x101;   
        public const int WM_SYSKEYDOWN = 0x104;   
        public const int WM_SYSKEYUP = 0x105;   
  
        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);   
        HookProc HookProcedure;   
  
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]   
        public static extern int SetWindowsHookEx(   
            int idHook, HookProc lpfn, IntPtr hInstance, int threadId);   
  
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]   
        public static extern bool UnhookWindowsHookEx(int idHook);   
  
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]   
        public static extern int CallNextHookEx(   
            int idHook, int nCode, Int32 wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool GetAsyncKeyState(int vKey   // 仮想キーコード
            );
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string name);

        [DllImport("kernel32.dll")]
        public static extern uint GetLastError();


        [StructLayout(LayoutKind.Sequential)]   
        public struct KeyboardHookStruct   
        {   
            public Int32 VKCode;   
            public Int32 ScanCode;   
            public Int32 Flags;   
            public Int32 Time;   
            public Int32 ExtraInfo;   
        }

        [Flags, Serializable]
        enum ModifierKeys : uint
        {
            None = 0,
            Alt = 1,
            Control = 2,
            Shift = 4,
            Windows = 8
        }
        public KeyboardHook()
        {
            Start();   
        }   
  
        ~KeyboardHook()   
        {   
            Stop();   
        }   
  
        public void Start()   
        {   
            if (hHook == 0)   
            {   
                HookProcedure = new HookProc(KeyBoardHookProcedure);   
  
                hHook = SetWindowsHookEx(   
                    WM_KEYBOARD_LL,   
                    HookProcedure,
                    //IntPtr.Zero,
                    Marshal.GetHINSTANCE(Assembly.GetCallingAssembly().GetModules(false)[0]),   
                    //GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName),
                    0);   
  
                if (hHook == 0)
                {
                    System.Diagnostics.Debug.WriteLine(GetLastError());
                    Stop();   
                   // throw new Exception("SetWindowsHookEx failed.");   
                }   
            }   
        }   
  
        public void Stop()   
        {   
            bool retMouse = true;   
            if (hHook != 0)   
            {   
                retMouse = UnhookWindowsHookEx(hHook);   
                hHook = 0;   
                if (retMouse == false)   
                {   
                   // throw new Exception("UnhookWindowsHookEx failed.");   
                }   
            }   
        }

        bool HasAltModifier(int flags) { return (flags & 0x20) == 0x20; } 
        protected int KeyBoardHookProcedure(int nCode, int wParam, IntPtr lParam)   
        {
            if (nCode >= 0)   
            {   
                KeyboardHookStruct KeyboardInfo =   
                    (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));

                Keys key = (Keys)KeyboardInfo.VKCode;

                Keys modifier = Keys.Modifiers;
;

                if (GetAsyncKeyState((int)Keys.LWin) || GetAsyncKeyState((int)Keys.RWin))
                    modifier |= Keys.LWin;
                if (GetAsyncKeyState((int)Keys.LShiftKey) || GetAsyncKeyState((int)Keys.RShiftKey))
                    modifier |= Keys.Shift;
                if (GetAsyncKeyState((int)Keys.LControlKey) || GetAsyncKeyState((int)Keys.RControlKey))
                    modifier |= Keys.Control;
                if (GetAsyncKeyState((int)Keys.Alt) )
                    modifier |= Keys.Alt;

                key |= modifier;

                switch (wParam)
                {
                    case WM_KEYDOWN:
                    case WM_SYSKEYDOWN:
                        {
                            if (KeyDown != null)
                            {
                                KeyDown(
                                    this,
                                    new KeyEventArgs(key)
                                );
                            }
                            break;
                        }
                    case WM_KEYUP:
                    case WM_SYSKEYUP:
                        {
                            if (KeyUp != null)
                            {
                                KeyUp(
                                    this,
                                     new KeyEventArgs(key)
                                );
                            }
                            break;
                        }
                    default:
                        break;
                }
            }   
            return CallNextHookEx(hHook, nCode, wParam, lParam);   
        }  
    }



}
